using Pinwheel.Kernel;
using Pinwheel.Service.Protocol;

namespace Pinwheel.Service
{
    
    public sealed partial class Cluster : ServiceBase
    {
        public const string Name = "Cluster";
        private const int MaxNode = 32;
        
        private ServerSocket m_ServerSocket;
        private ClientSocket m_ClientSocket;
        
        private Dictionary<string, PID> m_NodeMap = new Dictionary<string, PID>();
        
        public override void Create(IServiceManager serviceManager, IMessageQueue<ServiceMessage> messageQueue)
        {
            base.Create(serviceManager, messageQueue);
            m_ServerSocket = new ServerSocket(this, MaxNode, ServerSocket.DefaultBufferSize);
            m_ClientSocket = new ClientSocket(this);
        }

        public override int Init()
        {
            int code = base.Init();
            RegisterApi("StartListen", StartListen);
            RegisterApi("QueryNodeName", QueryNodeName);
            RegisterApi("QueryNode", QueryNode);
            RegisterApi("RegisterNode", RegisterNode);
            RegisterApi("UnregisterNode", UnregisterNode);
            
            RegisterApi("RegisterRemoteService", RegisterRemoteService);
            
            RegisterApi("Call", Call);
            RegisterApi("Send", Send);
            
            return code;
        }

        private async ValueTask<IMessage> StartListen(ServiceMessage message)
        {
            if (null! != ServiceManager.NodeList)
            {
                foreach (var node in ServiceManager.NodeList)
                {
                    if (node.Name == ServiceManager.NodeName)
                    {
                        m_ServerSocket.Listen(node.Port);
                    }
                    else
                    {
                        m_ClientSocket.Connect(node.Host, node.Port);
                    }
                }
                return Result.Success;
            }
            return Result.Failure;
        }
        
        private async ValueTask<IMessage> QueryNodeName(ServiceMessage message)
        {
            return new NodeInfo() {Pid = PID.Invalid, Name = ServiceManager.NodeName};
        }
        
        private async ValueTask<IMessage> QueryNode(ServiceMessage message)
        {
            if (message.Message is NodeInfo nodeInfo)
            {
                var nodeName = nodeInfo.Name;
                if (m_NodeMap.TryGetValue(nodeName, out var pid))
                {
                    nodeInfo.Pid = pid;
                    return nodeInfo;
                }
            }
            return null!;
        }
        
        private async ValueTask<IMessage> RegisterNode(ServiceMessage message)
        {
            if (message.Message is NodeInfo nodeInfo)
            {
                var pid = nodeInfo.Pid;
                var nodeName = nodeInfo.Name;
                Assert.Check(!string.IsNullOrEmpty(nodeName), $"[{Alias}]RegisterNode nodeName is null or empty.");

                var success = m_NodeMap.TryAdd(nodeName, pid);
                // Assert.Check(success, $"[{Alias}]RegisterNode nodeName exists: {nodeName}");
                
                Log.Info($"[{Node}][{Alias}]RegisterNode: {nodeName}");
                return success ? Result.Success : Result.Failure;
            }
            return Result.Failure;
        }

        private async ValueTask<IMessage> UnregisterNode(ServiceMessage message)
        {
            if (message.Message is NodeInfo nodeInfo)
            {
                var nodeName = nodeInfo.Name;
                Assert.Check(!string.IsNullOrEmpty(nodeName), $"[{Alias}]QueryNodeName response data nodeName is null or empty.");

                var success = m_NodeMap.Remove(nodeName, out _);
                Assert.Check(success, $"[{Alias}]QueryNodeName response data nodeName not exists: {nodeName}");
                
                Log.Info($"[{Node}][{Alias}]UnregisterNode: {nodeName}");
            }
            return null!;
        }
        
        private async ValueTask<IMessage> RegisterRemoteService(ServiceMessage message)
        {
            
            // ServiceManager.StartNewService<>()
            return null!;
        }
        
        
        private async ValueTask<IMessage> Call(ServiceMessage message)
        {
            if (message.Message is RemoteCall msg)
            {
                if (m_NodeMap.TryGetValue(msg.Node, out var node))
                {
                     return await node.Call(ServiceManager, m_PID, msg.Service, msg.Method, msg.Type, msg.Message);
                }
            }
            return null!;
        }
        
        
        private async ValueTask<IMessage> Send(ServiceMessage message)
        {
            if (message.Message is RemoteCall msg)
            {
                if (m_NodeMap.TryGetValue(msg.Node, out var node))
                {
                    node.Send(ServiceManager, m_PID, msg.Service, msg.Method, msg.Type, msg.Message);
                }
            }
            return null!;
        }
    }
}